﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Common
{
    public abstract class QueryParser
    {        
        public static string Or = "or", And = "and", Not = "not";
        public const char ExactMatchCharacter = '\"';
		public const string ExactMatchString = "\"";
        public static object[] ParseSearch(string searchString)
        {         
            List<string> orList, andList, notList, defaultList, splittedList;
            List<DistanceQuery> dQueries = new List<DistanceQuery>();
            orList = new List<string>();
            notList = new List<string>();
            andList = new List<string>();
            defaultList = andList;

			var splitted2 = searchString.Split(' ');

            var splitted = GetParsedPhraseArray(searchString, false);//searchString.Split(new char[]{' '},  StringSplitOptions.RemoveEmptyEntries); // assuming that the terms are separated by a single space
			splittedList = new List<string>(splitted);

            string lastOperator=string.Empty;

            for (int i = 0; i < splittedList.Count; i++)
            {
                var word = splittedList[i];
                if (word == Or || word == And || word == Not) { lastOperator = word; continue; }
			
                if (word.Contains('~')) dQueries.AddRange(DistanceQuery.FromWords(word));
                else if (lastOperator == Or) { orList.Add(word); }
                else if (lastOperator == Not) { notList.Add(word); }
                else if (lastOperator == And) { andList.Add(word); }
                else if (lastOperator == string.Empty) { defaultList.Add(word); }
                lastOperator = string.Empty;
            }

            return new object[]{andList, orList, notList, dQueries};
        }

        public static char[] ReplaceCharArray = { ' ', '?', ',', ';', ':', '.', '(', ')', '[', ']', '%', '*', '$', '-' };

        /// <summary>
        /// Parses a single word, removes unnecessary characters and stems the word (if in english)
        /// </summary>
        /// <param name="Text"></param>
        /// <returns></returns>
        public static string ParseWord(string Text, bool ForIndexing)
        {
            PorterStemmer stemmer = new PorterStemmer();
            //Strip text.
            Text = Text.Trim(ReplaceCharArray);
            if (ForIndexing)
                Text = Text.Trim('\"', '\''); //This should only be trimmed if appears before/after word.
            //Apply Stemmer (for english)
            Text = Text.ToLower();
            Text = stemmer.StemWord(Text);
            //Apply stopword filter - for english. (not using this, for now)
            // if (Stemmer.StopWordsHandler.IsStopword(Text))
            //    return String.Empty;
            return Text;
        }


        /// <summary>
        /// Parses a text phrase into an array (applies ParseWord to each word in the phrase).
        /// </summary>
        /// <param name="Text"></param>
        /// <returns></returns>

        public static string[] GetParsedPhraseArray(string Text, bool ForIndexing)
        {
            Regex r = new Regex(@"\s+");            //remove all whitespace
            Text = r.Replace(Text, " ");// to a single space
            foreach (char c in ReplaceCharArray)
                Text = Text.Replace(c, ' ');
            string[] PhraseArray = Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < PhraseArray.Length; i++)
            {
                PhraseArray[i] = ParseWord(PhraseArray[i], ForIndexing);
            }
            return PhraseArray;

        }
    }
    public class DistanceQuery
    {
        public static char Separator = '~';
        string _word1, _word2;
        int _distance;
        public string Word1 { get { return _word1; } }
        public string Word2 { get { return _word2; } }
        public int Distance { get { return _distance; } }
        public DistanceQuery(string w1, string w2, int d)
        {
            _word1 = w1;
            _word2 = w2;
            _distance = d;
        }
        public static DistanceQuery FromWord(string word)
        {
            string w1, w2;
            int d;
            int count = 0;
            foreach (char c in word) if (c == Separator) count++;
            if (count != 2) return null;
            var splitted = word.Split(Separator);
            if (splitted.Length != 3) return null;
            w1 = splitted[0];
            w2 = splitted[2];
            try
            {
                d = int.Parse(splitted[1]);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                return null; 
            }
            return new DistanceQuery(w1, w2, d);
        }
		public static DistanceQuery[] FromWords(string words)
		{
			DistanceQuery[] errorResult = new DistanceQuery[0];
			int count = 0, beginning, end;
			foreach (char c in words) if (c == Separator) count++;
			if (count % 2 != 0 || count < 1) return errorResult;
			count /= 2;
			DistanceQuery[] result = new DistanceQuery[count];
			beginning=0;
			for (int i = 0; i < count; i++)
			{ 
				int sepCounter=0;
				for(end=beginning;end<words.Length;end++)
				{
					if (words[end] == Separator) sepCounter++;
					if (sepCounter == 3) break;
				}
				string word = words.Substring(beginning, end - beginning);
				var res = DistanceQuery.FromWord(word);
				if (res == null) return errorResult;
				result[i] = res;
				beginning += word.Length-result[i].Word2.Length;
			}
			return result;
		}
		public override string ToString()
        {
            return string.Format("{0} <{1}> {2}",_word1,_distance,_word2);
        }
    }
}
